#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <system_error>
#include <functional>
#include <unordered_map>
#include <memory>
#include <cassert>

#include "Sock.hpp"
#include "Erro.hpp"
#include "Log.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace tcpServer
{
    class Connection;
    class TcpServer;

    const uint16_t default_port = 8080;
    const int default_fd = -1;

    using func_t = std::function<void(std::shared_ptr<tcpServer::Connection> )>;
    // using hander_t = std::function<void(const std::string&)>;

    class Connection
    {
    public:
        Connection(int sock, TcpServer* tsp)
            : _sock(sock), _tsp(tsp)
        {
        }

        void Register(func_t read, func_t write, func_t exec)
        {
            _recver = read;
            _sender = write;
            _excepter = exec;
        }

        void Close()
        {
            if (_sock > 0)
                close(_sock);
        }

    public:
        int _sock;              // 连接sock
        std::string _inbuffer;  // 输入缓冲区
        std::string _outbuffer; // 输出缓冲区

        func_t _recver;   // 从sock中读
        func_t _sender;   // 从sock中写
        func_t _excepter; // 处理 sock IO 中的异常

        TcpServer *_tsp;
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = default_port)
            : _port(port), _service(func)
        {
        }

        ~TcpServer()
        {
        }

        void initServer()
        {
            // 创建套接字
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();

            // 构建epoll
            _epoller.Create();

            addConnection(
                _sock.get_fd(),
                EPOLLIN | EPOLLET,
                // Accepter,
                std::bind(&TcpServer::Accepter, this, std::placeholders::_1),
                nullptr,
                nullptr);
        }

        void addConnection(int sock, uint32_t event, func_t recver, func_t sender, func_t execper)
        {
            // 设置非阻塞
            if (event & EPOLLET)
            {
                Util::setNonBlock(sock);
            }
            auto connect = std::make_shared<Connection>(sock, this);
            connect->Register(recver, sender, execper);

            // 添加事件
            bool n = _epoller.addEvent(sock, event);
            assert(n);

            // 添加到connecctions中管理
            _connections.insert({sock, connect});
        }

        void Accepter(std::shared_ptr<Connection> connect);
        void Recver(std::shared_ptr<Connection> connect);
        void Sender(std::shared_ptr<Connection> connect);
        void Execpter(std::shared_ptr<Connection> connect);

        void Loop()
        {
            int ready_num = _epoller.Wait(-1);
            for (int i = 0; i < ready_num; ++i)
            {
                int sock = _epoller._evens[i].data.fd;
                uint32_t event = _epoller._evens[i].events;

                // 将一场问题转化成读写问题，读写处理异常
                if (event & EPOLLERR)
                    event |= (EPOLLIN | EPOLLOUT);
                if (event & EPOLLHUP)
                    event |= (EPOLLIN | EPOLLOUT);

                // TODO
                // 事件派发
                if ((event & EPOLLIN) && (_connections.count(sock) > 0) && (_connections[sock]->_recver)) // 监听事件就绪
                    _connections[sock]->_recver(_connections[sock]);
                if ((event & EPOLLOUT) && (_connections.count(sock) > 0) && (_connections[sock]->_sender))
                    _connections[sock]->_sender(_connections[sock]);
            }
            logMessage(DEBUG, "timeout...");
        }

        void Dispatch()
        {
            while (true)
            {
                Loop();
            }
        }

        bool EnableRW(std::shared_ptr<Connection> conn, bool readable, bool writeable)
        {
            uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0);
            _epoller.Control(conn->_sock, EPOLL_CTL_MOD, event);
        }

    private:
        uint16_t _port;
        
        Sock _sock;
        Epoll _epoller;
        std::unordered_map<int, std::shared_ptr<Connection>> _connections;

        // hander_t _service; // 交给上层处理报文
        func_t _service; // 简略表达
    };

    void TcpServer::Accepter(std::shared_ptr<Connection> connect)
    {
        while (true)
        {
            std::string clientip;
            uint16_t clientport = 0;
            int sock = _sock.Accept(&clientip, &clientport);

            if (sock > 0)
            {
                addConnection(
                    sock,
                    EPOLLIN | EPOLLET,
                    std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                    std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                    std::bind(&TcpServer::Execpter, this, std::placeholders::_1));
                logMessage(NORMAL, "from [%s:%d] get a new link: %d", clientip.c_str(), clientport, sock);
            }
            else
            {
                if (EAGAIN == errno || EWOULDBLOCK == errno) // 资源未就绪
                    break;
                else if (EINTR == errno) // 如果有信号中断，再次读取
                    continue;
                else // 错误，暂不处理
                    break;
            }
        }
    }

    void TcpServer::Recver(std::shared_ptr<Connection> connect)
    {
        logMessage(DEBUG, "Recver in...");
        char buffer[1024];
        while (true)
        {
            logMessage(DEBUG, "recv start...");
            int n = recv(connect->_sock, buffer, sizeof(buffer)-1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                connect->_inbuffer = buffer;

                _service(connect);
                logMessage(DEBUG, "_service end...");
            }
            else if (n == 0)
            {
                if (connect->_excepter)
                {
                    connect->_excepter(connect);
                    return;
                }
            }
            else 
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (EINTR == errno)
                    continue;
                else 
                {
                    if (connect->_excepter)
                        connect->_excepter(connect);
                    logMessage(DEBUG, "Recver out1...");
                    return;
                } 
            }
        }
        logMessage(DEBUG, "Recver out2...");
    }

    void TcpServer::Sender(std::shared_ptr<Connection> connect)
    {
        logMessage(DEBUG, "Sender in ...");
        while (true)
        {
            ssize_t n = send(connect->_sock, connect->_outbuffer.c_str(), connect->_outbuffer.size(), 0);
            if (n > 0)
            {
                if (connect->_outbuffer.empty())
                    break;
                else 
                    connect->_outbuffer.erase(0, n);
            }
            else
            {
                if (EAGAIN == errno || EWOULDBLOCK == errno)
                    break;
                else if (EINTR == errno)
                    continue;
                else 
                {
                    if (connect->_excepter)
                        connect->_excepter(connect);
                    logMessage(DEBUG, "Sender out1 ...");
                    return;
                }
            }
        }

        if (!connect->_outbuffer.empty())
            connect->_tsp->EnableRW(connect, true, true);
        else 
            connect->_tsp->EnableRW(connect, true, false);
        
        logMessage(DEBUG, "Sender out2 ...");
    }

    void TcpServer::Execpter(std::shared_ptr<Connection> connect)
    {
        logMessage(DEBUG, "Execpter in ...");
        _epoller.Control(connect->_sock, EPOLL_CTL_DEL);
        connect->Close();
        _connections.erase(connect->_sock);
        logMessage(DEBUG, "Execpter out ...");
    }
}